-- 2022-8-17

--[[ 18.2 泛型for的用法

    上述那些迭代器有一个缺点 即需要为每个新的循环创建一个新的闭包 对于大多数情况而言 这或许不会有什么问题
    例如 在之前的allwords迭代器中 创建一个闭包的开销相对于读取整个文件的开销而言几乎可以忽略不计
    但是 在另外一些情况下 这样的开销可能会很可观 在这类情况中 我们可以通过使用泛型for自己保存迭代状态
    在本节中 我们会详细说明泛型for提供的用来保存状态的机制

    泛型for在循环过程中在其内部保存了迭代函数
    实际上 泛型for保存了三个值:一个迭代函数 一个不可变状态(invariant state)和一个控制变量(control variable) 下面让我们进一步学习
    泛型for的语法如下
    for var-list in exp-list do
        body
    end
    其中 var-list是由一个或多个变量名组成的列表 以逗号分隔
    exp-list是一个或多个表达式组成的列表 同样以逗号分隔
    通常 表达式列表只有一个元素 即一句迭代器工厂的调用
    例如 在如下代码中 变量列表是k,v 表达式列表只有一个元素pairs(t)
    for k,v in pairs(t) do print(k,v) end
    我们把变量列表的第一个(或唯一的)变量称为控制变量(control variable) 其值在循环过程中永远不会nil 因为当其值为nil时循环就结束了

    for做的第一件事情是对in后面的表达式求值 这些表达式应该返回三个值供for保存:迭代函数 不可变状态和控制变量的初始值
    类似于多重赋值 只有最后一个(或唯一的)表达式能够产生不止一个值 表达式列表的结果只会保留三个 多余的值会被丢弃 不足三个则以nil补齐
    例如 在使用简单迭代器时 工厂只会返回迭代函数 因此不可变状态和控制变量都是nil

    在上述的初始化步骤完成之后 for使用不可变状态和控制变量为参数来调用迭代函数 从for代码结构的立足点来看 不可变状态根本没有意义
    for只是把从初始化步骤得到的状态值传递给所有迭代函数 然后 for将迭代函数的返回值赋给变量列表中声明的变量
    如果第一个返回值(赋给控制变量的值)为nil 那么循环终止
    否则 for执行它的循环体并再次调用迭代函数 再不断的重复这个过程

    更确切的说 形如
    for var_1,...,var_n in explist do block end
    这样的代码结构与下列代码等价
    do
        local _f,_s,_var = explist
        while true do
            local var_1,...,var_n=_f(_s,_var)
            _var = var_1
            if _var == nil then break end
            block
        end
    end
    因此 假设迭代函数为f 不可变状态为s 控制变量的初始值为a0 那么在循环中控制变量的值依次为a1=f(s,a0)a2=f(s,a1) 依次类推 直至ai为nil
    如果for还有其他变量 那么这些变量只是简单地在每次调用f后得到额外的值
]]